import window from '@ohos.window';
import display from '@ohos.display';
import fs from '@ohos.file.fs';
import resourceManager from '@ohos.resourceManager';
import JsDataStore from './JsDataStore';
import { QtQPA } from './JsQtPlatform';
import JsLogger from './JsLogger';
import { JsWindowManager } from './JsWindowManager';
import { JsWindow } from './JsWindow';

class JsApplication {
  private mainWindow: window.Window = null;
  private mainWindowName: string = "opemharmony_qt_mainwindow";
  private qpa: any = QtQPA;

  getMainWindow(): window.Window {
    return this.mainWindow;
  }

  getMainWindowName(): string {
    return this.mainWindowName;
  }

  addWindow(name: string, window: JsWindow): void {
    let obj  = JsDataStore.getJsObjectLoader().createObject("JsWindowManager", "JsWindowManager");
    (obj as JsWindowManager).addWindow(name, window);
  }

  async run(windowStage: window.WindowStage) {
    let qtMajorVersion: number = this.qpa.qtMajorVersion();
    let QtCoreModule: any = null;
    if (qtMajorVersion == 5)
      QtCoreModule = await import ("libQt5Core.so");
    else if (qtMajorVersion == 6)
    QtCoreModule = await import ("libQt6Core.so");
    if (QtCoreModule == null) {
      JsLogger.fatal("Cannot load QtCore module");
      return;
    }
    JsDataStore.setQtMajorVersion(qtMajorVersion);
    let QtCore = QtCoreModule.default;
    QtCore.initJsObjectLoader((type: string, name: string, ...args: any[])=>{
      if (JsDataStore.getJsObjectLoader().hasJsObject(name))
        return JsDataStore.getJsObjectLoader().getJsObject(name)
      return JsDataStore.getJsObjectLoader().createObject(type, name, ...args);
    }, (name: string)=>{
      return JsDataStore.getJsObjectLoader().deleteJsObject(name);
    });
    JsDataStore.addQtNativeModule("QPA", this.qpa);
    JsDataStore.addQtNativeModule("QtCore", QtCore);
    this.mainWindow = windowStage.getMainWindowSync();
    let window = new JsWindow(this.mainWindowName, this.mainWindow);
    let localStore = new LocalStorage({"name": this.mainWindowName});
    await windowStage.loadContent('pages/Index', localStore);
    this.addWindow(this.mainWindowName, window);
    let p = this.mainWindow.getWindowProperties();
    let d = display.getDefaultDisplaySync();
    if (this.qpa != null) {
      this.qpa.setDisplayMetrics(p.windowRect.width, p.windowRect.height, d.densityDPI, d.scaledDensity, d.xDPI, d.yDPI,
        0, 0, p.windowRect.width, p.windowRect.height);
      this.qpa.setDeviceType(JsDataStore.deviceType());
      // this.qpa.setDisplayMetrics(d.width, d.height, d.densityDPI, d.scaledDensity, d.xDPI, d.yDPI,
      //   area.leftRect.left, area.topRect.height, d.width - area.leftRect.width - area.rightRect.width, d.height - area.topRect.height - area.bottomRect.height);
    }
    windowStage.on("windowStageEvent", (state)=>{
      if (this.qpa != null)
        this.qpa.updateApplicationState(state);
      JsLogger.info("window stage changed %{public}d", state);
    });
    JsDataStore.setWindowStage(windowStage);
    await this.extractFilesToCache();
    this.loadQtApplication();
  }

  quit() {
    this.qpa.quitQtApplication();
  }

  loadQtApplication() {
    this.qpa.setResourceManager(JsDataStore.getResourceManager());
    let result : boolean = this.qpa.startQtApplication(JsDataStore.getApplicationDirs(), "libqtmodule.so");
    JsLogger.info("load qt application result: %{public}s", result);
  }

  private saveFileToCache(file: resourceManager.RawFileDescriptor, des: string) {
    let paths = des.split("/").slice(0, -1);
    let dirs = JsDataStore.getApplicationDirs();
    let temp = dirs.cacheDir + "/";
    for (let i = 0; i < paths.length; ++i) {
      temp = temp + paths[i];
      let result = fs.accessSync(temp);
      if (!result) {
        fs.mkdirSync(temp);
      }
      temp = temp + "/";
    }

    let fileName = dirs.cacheDir + "/" + des;
    // 创建缓存文件(当前是覆盖式创建)
    let cacheFile = fs.openSync(fileName,
      fs.OpenMode.WRITE_ONLY | fs.OpenMode.CREATE | fs.OpenMode.TRUNC)
    let buffer = new ArrayBuffer(4096);
    let currentOffset = file.offset;
    let lengthNeedToReed = file.length;
    let readOption = {
      offset: currentOffset,
      length: lengthNeedToReed > buffer.byteLength ? 4096 : lengthNeedToReed
    }
    while (true) {
      // 读取buffer容量的内容
      let readLength = fs.readSync(file.fd, buffer, readOption);
      // 写入buffer容量的内容
      fs.writeSync(cacheFile.fd, buffer, {
        length: readLength
      })
      // 判断后续内容 修改读文件的参数
      if (readLength < 4096) {
        break;
      }
      lengthNeedToReed -= readLength;
      readOption.offset += readLength;
      readOption.length = lengthNeedToReed > buffer.byteLength ? 4096 : lengthNeedToReed;
    }
    fs.close(cacheFile);
  }

  async extractFile(src: string) {
    try {
      let R = JsDataStore.getResourceManager();
      let file: resourceManager.RawFileDescriptor = await R.getRawFd(src);
      this.saveFileToCache(file, src);
    } catch (err) {
      JsLogger.error("extract file failed: %{public}s %{public}s", JSON.stringify(err), src);
    }
  }

  async extractFilesToCache() {
    try {
      let R = JsDataStore.getResourceManager();
      let rawContent: Uint8Array = await R.getRawFileContent("qt.json");
      let str: string = String.fromCharCode.apply(null, rawContent)
      let files = JSON.parse(str);
      for (var i = 0; i < files.files.length; ++i) {
        await this.extractFile(files.files[i]);
      }
    } catch (err) {
      JsLogger.error("read file qt.json failed: %{public}s", JSON.stringify(err));
    }
  }
}

export default new JsApplication;